package es.efor.ics.gev.dao.jpa;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Repository;

import es.efor.ics.gev.dao.PersonaDao;
import es.efor.ics.gev.model.AtributoInformado;
import es.efor.ics.gev.model.AtributoMaestro;
import es.efor.ics.gev.model.AtributoPerfil;
import es.efor.ics.gev.model.GenericAtributoInformado;
import es.efor.ics.gev.model.Perfil;
import es.efor.ics.gev.model.PerfilPersona;
import es.efor.ics.gev.model.PerfilPersonal;
import es.efor.ics.gev.model.Persona;

/**
 * This class interacts with EntityManager to save/delete and retrieve Persona
 * objects.
 * 
 * @author dlisbona
 * 
 */
@Repository("personaDao")
public class PersonaDaoJpa extends GenericDaoJpa<Persona, Long> implements
		PersonaDao {

	/**
	 * Constructor that sets the entity to Persona.class.
	 */
	public PersonaDaoJpa() {
		super(Persona.class);
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonas() {
		return getEntityManager().createNamedQuery("findAllPersonas")
				.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByNombre(String nombre) {
		String value = '%' + nombre.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombre")
				.setParameter("nombre", value).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByPrimerApellido(String primerApellido) {
		String value = '%' + primerApellido.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByPrimerApellido")
				.setParameter("primerApellido", value).getResultList();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByNombreAndPrimerApellido(String nombre,
			String primerApellido) {
		String value1 = '%' + nombre.toUpperCase() + '%';
		String value2 = '%' + primerApellido.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombreAndPrimerApellido")
				.setParameter("nombre", value1)
				.setParameter("primerApellido", value2)
				.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByNombreAndPrimerApellidoAndPerfil(
			String nombre, String primerApellido, Long perfilId) {
		String value1 = '%' + nombre.toUpperCase() + '%';
		String value2 = '%' + primerApellido.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombreAndPrimerApellidoAndPerfilId")
				.setParameter("nombre", value1)
				.setParameter("primerApellido", value2)
				.setParameter("perfilId", perfilId)
				.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByNombreAndPrimerApellidoAndPerfil(
			String nombre, String primerApellido, String nombrePerfil) {
		String value1 = '%' + nombre.toUpperCase() + '%';
		String value2 = '%' + primerApellido.toUpperCase() + '%';
		String value3 = '%' + nombrePerfil.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombreAndPrimerApellidoAndNombrePerfil")
				.setParameter("nombre", value1)
				.setParameter("primerApellido", value2)
				.setParameter("nombrePerfil", value3)				
				.getResultList();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByPrimerApellidoAndPerfil(String primerApellido, String nombrePerfil) {
		String value1 = '%' + primerApellido.toUpperCase() + '%';
		String value2 = '%' + nombrePerfil.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByPrimerApellidoAndNombrePerfil")		
		.setParameter("primerApellido", value1)
		.setParameter("nombrePerfil", value2)				
		.getResultList();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByPrimerApellidoAndPerfil(
			String primerApellido, Long perfilId) {
		String value = '%' + primerApellido.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByPrimerApellidoAndPerfilId")		
		.setParameter("primerApellido", value)
		.setParameter("perfilId", perfilId)				
		.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")	
	public List<Persona> getPersonasByNombreAndPerfil(String nombre,
			Long perfilId) {
		String value = '%' + nombre.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombreAndPerfilId")		
		.setParameter("nombre", value)
		.setParameter("perfilId", perfilId)				
		.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByNombreAndPerfil(String nombre,
			String nombrePerfil) {
		String value1 = '%' + nombre.toUpperCase() + '%';
		String value2 = '%' + nombrePerfil.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombreAndNombrePerfil")		
		.setParameter("nombre", value1)
		.setParameter("nombrePerfil", value2)				
		.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByPerfil(Long perfilId) {		
		return getEntityManager().createNamedQuery("findPersonasByPerfilId")
				.setParameter("perfilId", perfilId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Persona> getPersonasByPerfil(String nombrePerfil) {
		String value = '%' + nombrePerfil.toUpperCase() + '%';
		return getEntityManager().createNamedQuery("findPersonasByNombrePerfil")
		.setParameter("nombrePerfil", value).getResultList();
	}	

	/**
	 * {@inheritDoc}
	 */
	public List<Persona> getPersonasByValorAtributo(Long atributoId, String valor) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public Persona createPersona(String nombre, String apellido1,
			String apellido2, List<PerfilPersonal> perfiles) {		
		Persona persona = getEntityManager().merge(
				new Persona(nombre, apellido1, apellido2));
		log.debug("new persona: " + persona.getNombreCompleto());
		persona.addPerfilesPersona(perfiles);
		getEntityManager().flush();
		return persona;
	}

	/**
	 * {@inheritDoc}
	 */
	public Persona savePersona(Persona persona) {
		log.debug("persona's id: " + persona.getId());
		Persona p = getEntityManager().merge(persona);
		getEntityManager().flush();
		return p;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Perfil> getPerfilesPersona(Long personaId) {
		return getEntityManager().createNamedQuery("findPerfilesPersona")
		.setParameter("id", personaId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	public Persona addPerfil(Persona persona, PerfilPersonal perfil) {
		if (persona.isPerfilAsociado(perfil.getId())) {
			throw new IllegalArgumentException("El perfil: " + perfil.getId()
					+ " ya se encuentra asociado a la persona");
		}
		persona.addPerfilPersona(perfil, persona.getNumPerfiles() + 1);
		getEntityManager().flush();
		return persona;
	}

	/**
	 * {@inheritDoc}
	 */
	public Persona removePerfil(Persona persona, PerfilPersonal perfil) {
		if (!persona.isPerfilAsociado(perfil.getId())) {
			throw new IllegalArgumentException("El perfil: " + perfil.getId()
					+ " no se encuentra asociado a la persona");
		}
		PerfilPersona perfilPersona = persona.getPerfilPersona(perfil.getId());		
		List<PerfilPersona> perfilesToUpdate = persona.getPerfilesOrderAbove(perfilPersona);		
		for (PerfilPersona perfilToUpdate : perfilesToUpdate) {
			perfilToUpdate.setOrden(perfilToUpdate.getOrden() - 1);
		}
		persona.removePerfilPersona(perfil);
		getEntityManager().flush();
		return persona;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public Persona updatePerfiles(Persona persona, List<PerfilPersonal> perfiles) {
		Collection<PerfilPersonal> perfilesToDelete = CollectionUtils.subtract(
				persona.getPerfiles(), perfiles);
		for (PerfilPersonal p : perfilesToDelete) {
			persona.removePerfilPersona(p);
		}

		for (int i = 0; i < perfiles.size(); i++) {
			int currentOrden = i + 1;
			PerfilPersonal perfil = perfiles.get(i);
			if (persona.isPerfilAsociado(perfil.getId())) {
				persona.getPerfilPersona(perfil.getId()).setOrden(currentOrden);
			} else {
				persona.addPerfilPersona(perfil, currentOrden);
			}
		}
		getEntityManager().flush();
		return persona;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoMaestro> getAtributosPersona(Long personaId) {
		return getEntityManager().createNamedQuery("findAtributosPersona")
				.setParameter("id", personaId).getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	public Integer getNextOrdenPerfiles(Long personaId) {
		return ((Long) getEntityManager().createNamedQuery(
				"findNextOrdenPerfilPersona").setParameter("id", personaId)
				.getSingleResult()).intValue();
	}

	/**
	 * {@inheritDoc}
	 */
	public Persona setValorAtributoPersona(Persona persona,
			AtributoMaestro atributo, String valor) {
		log.debug("Setting valor: " + valor + " - to Atributo: "
				+ atributo.getId() + " for Persona: " + persona);
		persona.setValorAtributo(atributo, valor);
		getEntityManager().flush();
		return persona;
	}

	/**
	 * {@inheritDoc}
	 */
	public Persona setValoresAtributosPersona(Persona persona,
			Map<AtributoMaestro, String> mapValores) {
		Set<Entry<AtributoMaestro, String>> entrySet = mapValores.entrySet();
		for (Iterator<Entry<AtributoMaestro, String>> it = entrySet.iterator(); it
				.hasNext();) {
			Map.Entry<AtributoMaestro, String> e = (Map.Entry<AtributoMaestro, String>) it
					.next();
			persona.setValorAtributo(e.getKey(), e.getValue());
		}
		getEntityManager().flush();
		return persona;
	}

	/**
	 * {@inheritDoc}
	 */
	public SortedMap<AtributoPerfil, String> getMapAtributosPersona(
			Persona persona, Perfil perfil) {
		SortedMap<AtributoPerfil, String> result = new TreeMap<AtributoPerfil, String>();
		for (AtributoPerfil atributoPerfil : perfil.getAtributosPerfil()) {
			result.put(atributoPerfil, persona
					.getValorAtributoInformado(atributoPerfil.getId()
							.getAtributoId()));
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<AtributoInformado> getValoresAtributosPerfil(Persona persona,
			Perfil perfil) {
		List<AtributoInformado> result = new ArrayList<AtributoInformado>();
		for (AtributoPerfil a : perfil.getAtributosPerfil()) {
			result.add(new GenericAtributoInformado(a, persona
					.getValorAtributoInformado(a.getId().getAtributoId())));
		}
		return result;
	}

}
